package com.kingmed.kmss.bpmn.flow.helper;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.kingmed.kmss.bpmn.flow.dto.ProcessInstanceDTO;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.kingmed.kmss.common.bo.bpmn.ProcessInstanceBO;
import com.kingmed.kmss.common.bo.bpmn.ProcessStarterBO;

import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class ProcessHelper {

	@Autowired
	ProcessEngine processEngine;
	
	@Autowired
	IdentityHelper idHelper;

	@Autowired
	RuntimeService runtimeService;
	

	public ProcessHelper(ProcessEngine processEngine) {
		super();
		this.processEngine = processEngine;
		this.runtimeService = this.processEngine.getRuntimeService();
	}

	public List<ProcessInstanceDTO> getProcessInstsByBizKey(String bizKey) {
		List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(bizKey).list();
	
		List<ProcessInstanceDTO> result = new ArrayList<ProcessInstanceDTO>();
		for (ProcessInstance inst : instances) {
			log.debug(inst.getBusinessKey() + ": " + inst.isEnded());
			ProcessInstanceDTO dto = new ProcessInstanceDTO();
			dto.setBusinessKey(inst.getBusinessKey());
			dto.setIsFinished(inst.isEnded());
			dto.setProcessInstanceId(inst.getProcessInstanceId());
			result.add(dto);
		}
		return result;
	}
	
	public List<ProcessInstanceDTO> getProcessInsts() {
		List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery().list();
	
		List<ProcessInstanceDTO> result = new ArrayList<ProcessInstanceDTO>();
		for (ProcessInstance inst : instances) {
			log.debug(inst.getBusinessKey() + ": " + inst.isEnded());
			ProcessInstanceDTO dto = new ProcessInstanceDTO();
			dto.setBusinessKey(inst.getBusinessKey());
			dto.setIsFinished(inst.isEnded());
			dto.setProcessInstanceId(inst.getProcessInstanceId());
			dto.setName(inst.getName());

			result.add(dto);
		}
		return result;
	}
	
	public int cleanupProcessInstByBizKey(String bizKey, String reason) {
		List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(bizKey).list();
	
		for (ProcessInstance inst : instances) {
			runtimeService.deleteProcessInstance(inst.getProcessInstanceId(), reason);
		}
		return instances.size();
	}
	public int cleanupAllProcessInsts(String reason) {
		List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery().list();
	
		for (ProcessInstance inst : instances) {
			runtimeService.deleteProcessInstance(inst.getProcessInstanceId(), reason);
		}
		return instances.size();
	}
	public List<String> getRuntimeDataId() {
		List<String> idList = new ArrayList<>();
		// 获取正在执行的任务列表
		List<Execution> list = runtimeService.createExecutionQuery().onlyProcessInstanceExecutions().list();
		list.forEach(execution -> {
			// 根据任务获取流程实例
			// 获取流程实例种的业务 key
			ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(execution.getProcessInstanceId()).singleResult();
			idList.add(pi.getBusinessKey());
		});
		
		return idList;
	}
	public ProcessInstance startProcessInstance(ProcessStarterBO startBO) {

		idHelper.setProcessInitator(startBO.getStartUserId());
		ProcessInstance processInstance = this.runtimeService.startProcessInstanceByKey(
				startBO.getProcDefKey(), startBO.getBusinessKey(), startBO.getProcVars().getVarMap());

		this.runtimeService.setProcessInstanceName(processInstance.getProcessInstanceId(), startBO.getInstanceName());

		log.info(
				"Process Instance information : Id<{}>, ProcessDefinitionId<{}>, Name<{}>, DefinitionKey<{}>, ProcessInstanceId<{}>, BusinessKey<{}>>",
				processInstance.getId(), processInstance.getProcessDefinitionId(), processInstance.getName(),
				processInstance.getProcessDefinitionKey(), processInstance.getProcessInstanceId(),
				processInstance.getBusinessKey());
		log.info("Process processKey<{}> started successfully.", startBO.getProcDefKey());

		return processInstance;
	}

	public void updateProcessInstance(ProcessInstanceBO piBO) {
		if (piBO.getInstanceName() != null && piBO.getInstanceName().length() > 0) {
			this.runtimeService.setProcessInstanceName(piBO.getInstanceId(), piBO.getInstanceName());
		}
	}

	public void setProcessVariables(String executionId, Map<String, Object> varMap) {
		this.runtimeService.setVariables(executionId, varMap);
	}

	public Map<String, Object> getProcessVariables(String executionId) {
		return this.runtimeService.getVariables(executionId);
	}

	@SuppressWarnings("unchecked")
	public <T> T getProcessVariable(String executionId, String varName) {
		Map<String, Object> vars = getProcessVariables(executionId);
		if (CollUtil.isEmpty(vars)) {
			return null;
		}
		
		return (T) vars.get(varName);
	}
	
	public Execution findExecutionInstanceById(String executionId) {
		Execution execution = this.runtimeService.createExecutionQuery().executionId(executionId).singleResult();
		log.info(" findExecutionInstanceById<{}> result is: {}", executionId, execution);
		return execution;
	}

	public List<Execution> findExecutionInstanceByProcessInstanceId(String processInstanceId) {
		List<Execution> executionList = this.runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();
		for (Execution execution : executionList) {
			if (execution.getParentId() == null) {
				executionList.remove(execution);
				break;
			}
		}
		log.info(" findExecutionInstanceByProcessInstanceId<{}> result is: {}", processInstanceId, executionList);
		return executionList;
	}

	public List<Execution> findExecutionInstanceByProcessDefinitionKey(String processInstanceKey) {

		List<Execution> executionList = this.runtimeService.createExecutionQuery()
				.processDefinitionKey(processInstanceKey).list();
		for (Execution execution : executionList) {
			if (execution.getParentId() == null) {
				executionList.remove(execution);
				break;
			}
		}
		log.info(" findExecutionInstanceByProcessKey<{}> result is: {}", processInstanceKey, executionList);
		return executionList;
	}

	public ProcessInstance getProcessInstance(String processInstanceId) {
		
		return runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
		
	}
	
	public ProcessInstance findProcessInstanceById(String processInstanceId) {
		ProcessInstance processInstance= this.runtimeService.createProcessInstanceQuery()
				.processInstanceId(processInstanceId).singleResult();
		log.info(" findProcessInstanceById<{}> result is: {}", processInstanceId, processInstance);
		return processInstance;
	}

	public List<ProcessInstance> findProcessInstanceByProcessDefinitionKey(String processDefinitionKey) {

		List<ProcessInstance> processInstanceList = this.runtimeService.createProcessInstanceQuery()
				.processDefinitionKey(processDefinitionKey).list();
		log.info(" findProcessInstanceByKey<{}> result is: {}", processDefinitionKey, processInstanceList);
		return processInstanceList;
	}

	public List<ProcessInstance> findProcessInstanceByCategory(String category) {

		List<ProcessInstance> processInstanceList = this.runtimeService.createProcessInstanceQuery()
				.processDefinitionCategory(category).list();
		log.info(" findProcessInstanceByCategory<{}> result is: {}", category, processInstanceList);
		return processInstanceList;
	}

	public List<ProcessInstance> findProcessInstanceByBusinessKey(String businessKey) {

		List<ProcessInstance> processInstanceList = this.runtimeService.createProcessInstanceQuery()
				.processInstanceBusinessKey(businessKey).list();
		log.info(" findProcessInstanceByBusinessKey<{}> result is: {}", businessKey, processInstanceList);
		return processInstanceList;
	}

	public void endProcess(ProcessInstanceBO processInstanceBO){
		this.runtimeService.deleteProcessInstance(processInstanceBO.getInstanceId(),processInstanceBO.getDeleteReason());
	}

	public int cleanupProcessInstByBizType(String bizType, String reason) {
		if (StringUtils.isEmpty(bizType)) {
			return -1;
		}
		List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery().list();
		
		int deletedRecord = 0;
		for (ProcessInstance inst : instances) {
			if (inst.getBusinessKey().contains(bizType)) {
				runtimeService.deleteProcessInstance(inst.getProcessInstanceId(), reason);		
				deletedRecord++;
			}
		}
		return deletedRecord;
	}
}
